﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using WebChatApi.Common.Interface;

/// <summary>
/// The Common namespace.
/// </summary>
namespace WebChatApi.Common
{
    /// <summary>
    /// Abstract class for SQL data access controller, which refers an entity in {T} type.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public abstract class SqlDataAccessController<T> : ISqlDataAdapter<T>, IDisposable
    {
        #region Constants

        /// <summary>
        /// The column_ key
        /// </summary>
        protected const string column_Key = "Key";

        /// <summary>
        /// The column_ code
        /// </summary>
        protected const string column_Code = "Code";

        /// <summary>
        /// The column_ stamp
        /// </summary>
        protected const string column_Stamp = "Stamp";

        /// <summary>
        /// The column_ password
        /// </summary>
        protected const string column_Password = "Password";

        /// <summary>
        /// The column_ new password
        /// </summary>
        protected const string column_NewPassword = "NewPassword";

        /// <summary>
        /// The column_ State
        /// </summary>
        protected const string column_State = "State";

        /// <summary>
        /// The column_ from stamp
        /// </summary>
        protected const string column_FromStamp = "FromStamp";

        /// <summary>
        /// The column_ to stamp
        /// </summary>
        protected const string column_ToStamp = "ToStamp";

        /// <summary>
        /// The column_ user key
        /// </summary>
        protected const string column_UserKey = "UserKey";

        /// <summary>
        /// The column_ created stamp
        /// </summary>
        protected const string column_CreatedStamp = "CreatedStamp";

        /// <summary>
        /// The column_ last updated stamp
        /// </summary>
        protected const string column_LastUpdatedStamp = "LastUpdatedStamp";

        /// <summary>
        /// The column_ source type
        /// </summary>
        protected const string column_SourceType = "SourceType";

        /// <summary>
        /// The column_ comments
        /// </summary>
        protected const string column_Comments = "Comments";

        /// <summary>
        /// The column_ operator key
        /// </summary>
        protected const string column_OperatorKey = "OperatorKey";

        /// <summary>
        /// The column_ token
        /// </summary>
        protected const string column_Token = "Token";

        /// <summary>
        /// The column_ avator code
        /// </summary>
        protected const string column_AvatorCode = "AvatorCode";

        /// <summary>
        /// The column_ service stamp
        /// </summary>
        protected const string column_ServiceStamp = "ServiceStamp";

        /// <summary>
        /// The column_ expired stamp
        /// </summary>
        protected const string column_ExpiredStamp = "ExpiredStamp";

        /// <summary>
        /// The session life cycle
        /// </summary>
        protected const string column_Id = "Id";

        /// <summary>
        /// The column_ name
        /// </summary>
        protected const string column_Name = "Name";

        /// <summary>
        /// The column_ culture info
        /// </summary>
        protected const string column_CultureInfo = "CultureInfo";

        /// <summary>
        /// The column_ email
        /// </summary>
        protected const string column_Email = "Email";

        /// <summary>
        /// The column_ cell phone
        /// </summary>
        protected const string column_CellPhone = "CellPhone";

        /// <summary>
        /// The column_ first name
        /// </summary>
        protected const string column_FirstName = "FirstName";

        /// <summary>
        /// The column_ last name
        /// </summary>
        protected const string column_LastName = "LastName";

        /// <summary>
        /// The column_ middle name
        /// </summary>
        protected const string column_MiddleName = "MiddleName";

        /// <summary>
        /// The column_ birthday
        /// </summary>
        protected const string column_Birthday = "Birthday";

        /// <summary>
        /// The column_ gender
        /// </summary>
        protected const string column_Gender = "Gender";

        /// <summary>
        /// The column_ device id
        /// </summary>
        protected const string column_DeviceId = "DeviceId";

        /// <summary>
        /// The column_ agent
        /// </summary>
        protected const string column_Agent = "Agent";

        /// <summary>
        /// The column_ ip address
        /// </summary>
        protected const string column_IpAddress = "IpAddress";

        /// <summary>
        /// The column_ refer
        /// </summary>
        protected const string column_Refer = "Refer";

        /// <summary>
        /// The column_ data XML
        /// </summary>
        protected const string column_DataXml = "DataXml";

        /// <summary>
        /// The column_ ciphertext
        /// </summary>
        protected const string column_Ciphertext = "Ciphertext";

        /// <summary>
        /// The column_ target blog key
        /// </summary>
        protected const string column_TargetBlogKey = "TargetBlogKey";

        /// <summary>
        /// The column_ target user key
        /// </summary>
        protected const string column_TargetUserKey = "TargetUserKey";

        /// <summary>
        /// The column_ content
        /// </summary>
        protected const string column_Content = "Content";

        /// <summary>
        /// The column_ content
        /// </summary>
        protected const string column_Keyword = "Keyword";

        /// <summary>
        /// The column_ count
        /// </summary>
        protected const string column_Count = "Count";

        /// <summary>
        /// The column_ address
        /// </summary>
        protected const string column_Address = "Address";

        /// <summary>
        /// The column_ parent key
        /// </summary>
        protected const string column_ParentKey = "ParentKey";

        /// <summary>
        /// The column_ display name
        /// </summary>
        protected const string column_DisplayName = "DisplayName";

        /// <summary>
        /// The column_ source name
        /// </summary>
        protected const string column_SourceName = "SourceName";

        /// <summary>
        /// The column_ source identity
        /// </summary>
        protected const string column_SourceIdentity = "SourceIdentity";

        /// <summary>
        /// The column_ changeset
        /// </summary>
        protected const string column_ChangesetId = "ChangesetId";

        /// <summary>
        /// The column_ cured time
        /// </summary>
        protected const string column_CuredTime = "CuredTime";

        /// <summary>
        /// The column_ order desc
        /// </summary>
        protected const string column_OrderDesc = "OrderDesc";

        /// <summary>
        /// The column_ third party token
        /// </summary>
        protected const string column_ThirdPartyToken = "ThirdPartyToken";

        /// <summary>
        /// The column_ third party identity
        /// </summary>
        protected const string column_ThirdPartyIdentity = "ThirdPartyIdentity";

        /// <summary>
        /// The column_ category
        /// </summary>
        protected const string column_Category = "Category";

        /// <summary>
        /// The column_ categoryKey
        /// </summary>
        protected const string column_CategoryKey = "CategoryKey";

        /// <summary>
        /// The column_ target category
        /// </summary>
        protected const string column_TargetCategory = "TargetCategory";

        /// <summary>
        /// The column_ description
        /// </summary>
        protected const string column_Description = "Description";

        /// <summary>
        /// The column_ last sync stamp
        /// </summary>
        protected const string column_LastSyncStamp = "LastSyncStamp";

        /// <summary>
        /// The column_ interface name
        /// </summary>
        protected const string column_InterfaceName = "InterfaceName";

        /// <summary>
        /// The column_ visible only
        /// </summary>
        protected const string column_VisibleOnly = "VisibleOnly";

        /// <summary>
        /// The column_ role
        /// </summary>
        protected const string column_Role = "Role";

        /// <summary>
        /// The column_ permission
        /// </summary>
        protected const string column_Permission = "Permission";

        /// <summary>
        /// The column_ unread chart count
        /// </summary>
        protected const string column_UnreadChartCount = "UnreadChartCount";

        /// <summary>
        /// The column_ unread message count
        /// </summary>
        protected const string column_UnreadMessageCount = "UnreadMessageCount";

        /// <summary>
        /// The column_ geography tier key
        /// </summary>
        protected const string column_GeographyTierKey = "GeographyTierKey";

        /// <summary>
        /// The column_ department key
        /// </summary>
        protected const string column_DepartmentKey = "DepartmentKey";

        /// <summary>
        /// The column_ hospital key
        /// </summary>
        protected const string column_HospitalKey = "HospitalKey";

        /// <summary>
        /// The column_ page
        /// </summary>
        protected const string column_Page = "Page";

        /// <summary>
        /// The column_ tag
        /// </summary>
        protected const string column_Tag = "Tag";

        /// <summary>
        /// The column_ target
        /// </summary>
        protected const string column_Target = "Target";

        /// <summary>
        /// The column_ drug remind key
        /// </summary>
        protected const string column_DrugRemindKey = "DrugRemindKey";

        /// <summary>
        /// The column_ last retrieved key
        /// </summary>
        protected const string column_LastRetrievedKey = "LastRetrievedKey";

        /// <summary>
        /// The column_ uploaded stamp
        /// </summary>
        protected const string column_UploadedStamp = "UploadedStamp";

        /// <summary>
        /// The column_ validated state
        /// </summary>
        protected const string column_ValidatedState = "ValidatedState";

        /// <summary>
        /// The column_ validated stamp
        /// </summary>
        protected const string column_ValidatedStamp = "ValidatedStamp";

        /// <summary>
        /// The column_ hospital name
        /// </summary>
        protected const string column_HospitalName = "HospitalName";

        /// <summary>
        /// The column_ hash sh a1
        /// </summary>
        protected const string column_HashSHA1 = "HashSHA1";

        /// <summary>
        /// The column_ created by
        /// </summary>
        protected const string column_CreatedBy = "CreatedBy";

        /// <summary>
        /// The column_ validation comments
        /// </summary>
        protected const string column_ValidationComments = "ValidationComments";

        /// <summary>
        /// The column_ office tel
        /// </summary>
        protected const string column_OfficeTel = "OfficeTel";        
        
        /// <summary>
        /// The session life cycle
        /// </summary>
        protected int SessionLifeCycle;

        /// <summary>
        /// The reset password token life cycle
        /// </summary>
        protected int ResetPasswordTokenLifeCycle;

        #endregion

        /// <summary>
        /// The database operator
        /// </summary>
        protected DatabaseOperator databaseOperator = null;

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlDataAccessController{T}" /> class.
        /// </summary>
        /// <param name="sqlConnectionString">The SQL connection string.</param>
        public SqlDataAccessController(string sqlConnectionString)
        {
            this.databaseOperator = new DatabaseOperator(sqlConnectionString);
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SqlDataAccessController{T}" /> class.
        /// </summary>
        /// <param name="sqlConnection">The SQL connection.</param>
        public SqlDataAccessController(SqlConnection sqlConnection)
        {
            this.databaseOperator = new DatabaseOperator(sqlConnection);
        }

        #endregion


        /// <summary>
        /// Converts the object.
        /// NOTE: In this method, reader would be load entirely by <c>while</c> statements and be closed finally.
        /// If you need to override it, or use other method instead, please do remember to close reader manually.
        /// </summary>
        /// <param name="sqlDataReader">The SQL data reader.</param>
        /// <returns>List{`0}.</returns>
        /// <exception cref="OperationFailureException">ConvertObject</exception>
        public List<T> ConvertObject(SqlDataReader sqlDataReader)
        {
            sqlDataReader.CheckNullObject("sqlDataReader");
            List<T> result = new List<T>();

            try
            {
                while (sqlDataReader.Read())
                {
                    result.Add(ConvertEntityObject(sqlDataReader));
                }
            }
            catch (Exception ex)
            {
                throw new OperationFailureException("ConvertObject", ex);
            }
            finally
            {
                if (sqlDataReader != null)
                {
                    sqlDataReader.Close();
                }
            }

            return result;
        }

        /// <summary>
        /// Converts the entity object.
        /// </summary>
        /// <param name="sqlDataReader">The SQL data reader.</param>
        /// <returns>Object instance in type {`0}.</returns>
        protected abstract T ConvertEntityObject(SqlDataReader sqlDataReader);

        #region Fill interface based object.

        /// <summary>
        /// Fills the base object fields.
        /// </summary>
        /// <param name="baseObj">The base obj.</param>
        /// <param name="sqlDataReader">The SQL data reader.</param>
        protected virtual void FillBaseObjectFields(BaseObject baseObj, SqlDataReader sqlDataReader)
        {
            if (baseObj != null)
            {
                baseObj.Key = sqlDataReader[column_Key].DBToGuid(Guid.NewGuid()).Value;
                baseObj.CreatedStamp = sqlDataReader[column_CreatedStamp].DBToDateTime(DateTime.UtcNow);
                baseObj.LastUpdatedStamp = sqlDataReader[column_LastUpdatedStamp].DBToDateTime(DateTime.UtcNow);
                baseObj.State = (ObjectState)(sqlDataReader[column_State].DBToInt32(0));
            }
        }

        /// <summary>
        /// Fills the base object fields.
        /// </summary>
        /// <param name="baseContentObject">The base content object.</param>
        /// <param name="sqlDataReader">The SQL data reader.</param>
        protected virtual void FillBaseContentObjectFields(BaseContentObject baseContentObject, SqlDataReader sqlDataReader)
        {
            if (baseContentObject != null)
            {
                FillBaseObjectFields(baseContentObject, sqlDataReader);
                //baseContentObject.UserDisplayName;
                //baseObj.Key = sqlDataReader[column_Key].DBToGuid(Guid.NewGuid());
                //baseObj.CreatedStamp = sqlDataReader[column_CreatedStamp].DBToDateTime(DateTime.UtcNow);
                //baseObj.LastUpdatedStamp = sqlDataReader[column_LastUpdatedStamp].DBToDateTime(DateTime.UtcNow);
                //baseObj.State = (ObjectState)(sqlDataReader[column_State].DBToInt32(0));
            }
        }

        #endregion

        /// <summary>
        /// Generates the name of the SQL sp parameter.
        /// </summary>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="parameterObject">The parameter object.</param>
        /// <returns>SqlParameter.</returns>
        protected SqlParameter GenerateSqlSpParameter(string columnName, object parameterObject)
        {
            return new SqlParameter("@" + columnName.Trim(), parameterObject == null ? Convert.DBNull : parameterObject);
        }


        /// <summary>
        /// Generates the name of the SQL sp parameter.
        /// </summary>
        /// <param name="columnName">Name of the column.</param>
        /// <param name="parameterObject">The parameter object.</param>
        /// <returns>SqlParameter.</returns>
        protected SqlParameter GenerateSqlSpParameter(string columnName, bool parameterObject)
        {
            return GenerateSqlSpParameter(columnName, parameterObject ? 1 : 0);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            if (this.databaseOperator != null)
            {
                this.databaseOperator.Dispose();
            }
        }

        /// <summary>
        /// Combines the names.
        /// </summary>
        /// <param name="firstName">The first name.</param>
        /// <param name="middleName">Name of the middle.</param>
        /// <param name="lastName">The last name.</param>
        /// <returns>System.String.</returns>
        public static string CombineNames(string firstName, string middleName, string lastName)
        {
            string result = !string.IsNullOrWhiteSpace(firstName) ? firstName : string.Empty;

            if (!string.IsNullOrWhiteSpace(middleName))
            {
                result += " " + middleName;
            }

            if (!string.IsNullOrWhiteSpace(lastName))
            {
                result += " " + lastName;
            }


            return result;
        }

        /// <summary>
        /// Combines the names.
        /// </summary>
        /// <param name="sqlDataReader">The SQL data reader.</param>
        /// <returns>System.String.</returns>
        protected static string CombineNames(SqlDataReader sqlDataReader)
        {
            return CombineNames(
                              sqlDataReader[column_FirstName].DBToString(),
                              sqlDataReader[column_MiddleName].DBToString(),
                              sqlDataReader[column_LastName].DBToString());
        }
    }
}
